package arrayAndSlices

import "fmt"

/** 数组和切片 */

// 数组示例1
func arrayCase1() {
	// 定义固定长度数组
	var nums [3]int
	fmt.Println(nums)
	// 数组赋值
	nums[0] = 1
	nums[1] = 2
	nums[2] = 3
	fmt.Println(nums)

	// 初始话固定长度数组
	ls1 := [3]int{23, 3, 12}
	fmt.Println("ls1 = ", ls1, "ls1 长度= ", len(ls1))

	// 声明数组忽略声明数组的长度，并用 ... 代替，让编译器为你自动计算长度,计算后也是固定长度数组
	ls2 := [...]int{23, 3, 5, 34}
	fmt.Println("ls2 = ", ls2, "ls2 长度= ", len(ls2))

}

// 遍历数组 len() 长度来遍历
func arrayCase2() {
	list1 := [...]string{"丽丽", "安康", "小米", "小红", "罗列"}
	for i := 0; i < len(list1); i++ {
		fmt.Println(list1[i])
	}
}

// 遍历数组 range 的方式
func arrayCase3() {
	list1 := [...]float64{23.2, 11.20, 23.02, 23, 90, 43.901}
	for i, v := range list1 {
		fmt.Println(i, v)
	}

	// 如果你只需要值并希望忽略索引，则可以通过用 _ 空白标识符替换索引来执行。
	for _, v := range list1 {
		fmt.Println(v)
	}
}

// 遍历二维数组
func arrayCase4() {
	// 二维数组1
	a := [3][2]string{{"lion", "tiger"}, {"cat", "dog"}, {"pigeon", "peacock"}}
	for _, v1 := range a {
		for _, v2 := range v1 {
			fmt.Printf("%s ", v2)
		}
		fmt.Printf("\n")
	}

	fmt.Println("--------------------------------------------")

	// 二维数组2
	var b [3][2]string
	b[0][0] = "apple"
	b[0][1] = "samsung"
	b[1][0] = "microsoft"
	b[1][1] = "google"
	b[2][0] = "AT&T"
	b[2][1] = "T-Mobile"
	for _, v1 := range b {
		for _, v2 := range v1 {
			fmt.Printf("%s ", v2)
		}
		fmt.Printf("\n")
	}
}

// 切片示例1
func slicesCase1() {
	list1 := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	// 使用切片：取出 4，5，6 元素
	var ls1 []int = list1[2:5]
	fmt.Println(ls1, "3的下标是2， 下标5是不包含元素6的")

	// 创建切片 方式二
	ls2 := []int{6, 7, 8}
	fmt.Println(ls2)

	// 修改切片中的元素，会反应在底层数组中
	list2 := [...]int{11, 22, 33, 44, 55, 66, 77, 88, 99}
	ls3 := list2[2:5]
	//fmt.Println(ls3) //[33 44 55]
	for i, v := range ls3 {
		ls3[i]++
		fmt.Println(i, v, ls3[i])
	}
	fmt.Println(list2) //底层数组的影响变动  [11 22 34 45 56 66 77 88 99]

	// 多个切片公用同一个底层数组
	list3 := [5]int{1, 2, 3, 4, 5}
	ls4 := list3[:]
	ls5 := list3[:]
	fmt.Println(ls4)
	fmt.Println(ls5)
	ls4[0] = 1 * 5
	fmt.Println(list3) //[5 2 3 4 5]
	ls5[1] = 2 * 5
	fmt.Println(list3) //[5 10 3 4 5]
	ls5[0] = 11
	fmt.Println(list3) //[11 10 3 4 5]
	fmt.Println(list3) //[11 10 3 4 5]

	// 切片的长度和容量
	var list4 [5]string = [5]string{"a", "b", "c", "d", "e"}
	ls6 := list4[2:5]
	fmt.Printf("list4 = %s; 长度 = %d; 容量 = %d \n", list4, len(list4), cap(list4))
	fmt.Printf("ls6 = %s; 长度 = %d; 容量 = %d \n", ls6, len(ls6), cap(ls6))

	// 创建切片 方式三 make(数组类型, 长度， 容量[可选值默认值为长度值])
	list6 := make([]int, 5, 5)
	fmt.Println(list6)

	// 切片追加元素
	list7 := []string{"lili", "zhangsan", "wangwu"}
	fmt.Printf("list7 = %s; 长度 = %d; 容量 = %d \n", list7, len(list7), cap(list7))
	// append(数组, param) 当追加的元素超过数组存储长度，会自动在底层数组基础上扩容一倍
	list7 = append(list7, "tom")
	fmt.Printf("list7 = %s; 长度 = %d; 容量 = %d \n", list7, len(list7), cap(list7))

	// 切片类型 nil 发音nǎo nil等于0值
	var names []string
	if names == nil {
		// 切片类型是0值
		fmt.Printf("names = %s; 长度 = %d; 容量 = %d \n", names, len(names), cap(names))
		names = append(names, "LiLi", "ZhangSan")
		fmt.Printf("names = %s; 长度 = %d; 容量 = %d \n", names, len(names), cap(names))
	}

	// 切片和切片之间的添加
	ls7 := []int{1, 2}
	ls8 := []int{3, 4}
	ls9 := append(ls7, ls8...)
	fmt.Println(ls9) //[1 2 3 4]
	ls10 := append(ls8, ls7...)
	fmt.Println(ls10) //[3 4 1 2]

	// 多维切片 并对多维切片遍历
	pls := [][]string{{"C", "C++"}, {"JavaScript"}, {"Go", "Rust"}}
	for _, v1 := range pls {
		for _, v2 := range v1 {
			fmt.Printf("%s ", v2)
		}
		fmt.Println()
	}
}

// 切片示例2 内存优化🔥
// 🌰举个案例：假设有一个非常大的数组，我们只想处理它的一小部分，然后根据这个大数组创建切片进行操作，
// 注意：这时大数组还在内存中处于引用无法被释放；
func slicesCase2() {
	// 模拟大数组
	names := []string{"小敏", "小红", "小张", "小兰", "小绿", "小吕", "小欢", "小咖"}
	// 取出要处理的部分
	sls1 := names[2:5]
	fmt.Printf("模拟大数组 = %s;  \n", names)
	fmt.Printf("取出要处理的部分 = %s;  \n", sls1)

	// 👍方式1 使用copy 函数创建独立切片
	// 创建新的独立切片，断开与原数组的引用
	newSlice := make([]string, len(sls1))
	copy(newSlice, sls1)
	fmt.Printf("新的切片 = %s;  \n", newSlice)
	// 修改切片，查看是否和之前的数组还有引用关系
	newSlice[0] = "lili-2"
	fmt.Printf("原有大数组 = %s;  \n", names)
	fmt.Printf("取出要处理的部分 = %s;  \n", sls1)
	fmt.Printf("处理后新的切片数组 = %s;  \n", newSlice)

	// 👍方式2 使用 append 创建独立切片
	newSli2 := append([]string(nil), sls1...)
	fmt.Printf("新的切片2 = %s;  \n", newSli2)
	// 修改切片，查看是否和之前的数组还有引用关系
	newSli2[1] = "lili-2"
	fmt.Printf("原有大数组 = %s;  \n", names)
	fmt.Printf("取出要处理的部分 = %s;  \n", sls1)
	fmt.Printf("处理后新的切片数组 = %s;  \n", newSli2)

}

func Test() {

	// 数组示例
	arrayCase1()

	arrayCase2()

	arrayCase3()

	arrayCase4()

	// 切片示例
	slicesCase1()

	slicesCase2()

}
